
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>

/**
 * 生产者消费者。生产者生产一个数据，通知消费者取数据。
 * 消费者取完数据通知生产者生产，交替而行。
*/

typedef struct
{
    int res;

    int p_wait; // 等待标志-生产者
    int c_wait; // 等待标志-消费者

    pthread_cond_t pCond; // 生产者条件变量
    pthread_cond_t cCond; // 消费者条件变量

    pthread_mutex_t pMutex; // 生产者互斥量
    pthread_mutex_t cMutex; // 消费者互斥量

} Result;

// 生产者
void *productor(void *arg)
{

    Result *r = (Result *)arg;

    int i = 1;
    for (; i <= 100; ++i)
    {
        // 查看消费者是否准备好
        pthread_mutex_lock(&r->cMutex);
        while (!r->c_wait)
        {
            pthread_mutex_unlock(&r->cMutex);
            sleep(1);
            pthread_mutex_lock(&r->cMutex);
        }
        // 消费者以就绪，写入数据
        r->res = i + 100;
        printf("Productor(%lx) write data %d\n", pthread_self(), r->res);
        r->c_wait = 0;
        pthread_mutex_unlock(&r->cMutex);
        pthread_cond_broadcast(&r->cCond);

        pthread_mutex_lock(&r->pMutex);
        r->p_wait = 1;
        pthread_cond_wait(&r->pCond, &r->pMutex); // 等待消费者通知
        pthread_mutex_unlock(&r->pMutex);
    }

    return (void *)0;
}

// 消费者
void *consumer(void *arg)
{
    Result *r = (Result *)arg;

    int i = 1;
    for (; i <= 100; ++i)
    {
        pthread_mutex_lock(&r->cMutex);
        r->c_wait = 1;
        pthread_cond_wait(&r->cCond, &r->cMutex); // 告知生产者，消费者以准备就绪，然后等待生产者通知
        printf("Consumer(%lx) read data %d\n", pthread_self(), r->res);
        pthread_mutex_unlock(&r->cMutex);

        pthread_mutex_lock(&r->pMutex);
        while (!r->p_wait)
        {
            pthread_mutex_unlock(&r->pMutex);
            sleep(1);
            pthread_mutex_lock(&r->pMutex);
        }
        r->p_wait = 0;
        pthread_mutex_unlock(&r->pMutex);
        pthread_cond_broadcast(&r->pCond);
    }

    return (void *)0;
}

int main()
{
    pthread_t pPid, cPid;

    Result result;
    result.p_wait = 0;
    result.c_wait = 0;
    pthread_cond_init(&result.cCond, NULL);
    pthread_cond_init(&result.pCond, NULL);
    pthread_mutex_init(&result.pMutex, NULL);
    pthread_mutex_init(&result.cMutex, NULL);

    if (pthread_create(&pPid, NULL, productor, &result) != 0)
    {
        fprintf(stderr, "productor pthread create error!\n");
        exit(EXIT_FAILURE);
    }

    if (pthread_create(&cPid, NULL, consumer, &result) != 0)
    {
        fprintf(stderr, "consumer pthread create error!\n");
        exit(EXIT_FAILURE);
    }

    pthread_join(pPid, NULL);
    pthread_join(cPid, NULL);

    pthread_cond_destroy(&result.cCond);
    pthread_cond_destroy(&result.pCond);
    pthread_mutex_destroy(&result.pMutex);
    pthread_mutex_destroy(&result.cMutex);

    return 0;
}